/*******************************************************************************
*                                                                              *
*                               Kyoto University                               *
*                               Okuno laboratory                               *
*                                    Tabulæ                                    *
*                          Louis-Kenzo Furuya Cahier                           *
*                                                                              *
********************************************************************************
*                                                                              *
* Copyright (c) 2012 Louis-Kenzo Furuya Cahier                                 *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a copy *
* of this software and associated documentation files (the "Software"), to     *
* deal in the Software without restriction, including without limitation the   *
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or  *
* sell copies of the Software, and to permit persons to whom the Software is   *
* furnished to do so, subject to the following conditions:                     *
*                                                                              *
* The above copyright notice and this permission notice shall be included in   *
* all copies or substantial portions of the Software.                          *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR   *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER       *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS *
* IN THE SOFTWARE.                                                             *
*                                                                              *
*******************************************************************************/

#ifndef TABULAE_LOGGER
#define TABULAE_LOGGER

/********************************** Includes **********************************/
// Standard library
#include <string>
#include <iostream>
#include <fstream>
// Boost
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/thread.hpp"
#include "boost/thread/mutex.hpp"
// Project
#include "Logging.h"

/************************************ Main ************************************/
namespace Tabulae {
	class TimeStamp {
		private:
			//====================> Properties
			boost::posix_time::ptime _time;
			
		public:
			//====================> Class variables
			static TimeStamp Now;
			
			//====================> Constructors
			TimeStamp();
			TimeStamp(const boost::posix_time::ptime& time);
			
			//====================> Class methods
			static TimeStamp now();
			
			//====================> Public methods
			boost::posix_time::ptime time() const;
	};
	
	class Location {
		private:
			//====================> Properties
			std::string _location;
			
		public:
			//====================> Constructors
			Location();
			Location(const std::string& location);
			
			//====================> Public methods
			const std::string& string() const;
	};
	
	class Logger {
		private:
			//====================> Properties
			LoggingLevel   _level;
			boost::shared_ptr<std::ofstream> _internal_stream;
			std::ostream*  _external_stream;
			bool           _auto_timestamp;
			bool           _relative_timestamp;
			bool           _auto_timestamp_inhibited;
			boost::posix_time::ptime         _last_timestamp;
			boost::posix_time::time_duration _last_elapsed;
			std::string                      _last_location;
			boost::thread::id                _last_thread_id;
			
			//====================> Class properties (private)
			static LoggingLevel minimum_logging_level;
			static boost::mutex access_mutex;
			static boost::mutex line_printing_mutex;
			static bool line_printing;
			
			//====================> Class helpers (private)
			void lock_line_printing();
			void unlock_line_printing();
		
		public:
			//====================> Class variables (public)
			static Logger Trace;
			static Logger Debug;
			static Logger Info;
			static Logger Warning;
			static Logger Error;
			static Logger Fatal;
			
			//====================> Class methods
			static void log_level(LoggingLevel level);
		
		public:
			//====================> Constructors
			Logger(LoggingLevel level = ::Tabulae::Trace);
			Logger(const Logger& logger);
			
			//====================> Public methods
			template <typename T>
			Logger& operator<<(T input);
			
			template <typename T>
			Logger& operator<<(T* input);
			
			Logger& operator<<(std::ostream& (*input)(std::ostream&));
			Logger& operator<<(std::ios& (*input)(std::ios&));
			Logger& operator<<(std::ios_base& (*input)(std::ios_base&));
			
			Logger& operator<<(const TimeStamp& timestamp);
			Logger& operator<<(const Location& location);
			
			void set_output(std::ostream& external_stream);
			void set_output(const std::string& path);
			void set_output(const char* path);
			
			void auto_timestamp(bool auto_timestamp = true);
			void relative_timestamp(bool relative_timestamp = true);
		
		private:
			//====================> Helpers
			bool is_internal_stream() const;
			bool is_external_stream() const;
			std::ostream& stream();
			void destroy_internal_stream_reference_if_necessary();
			bool has_stream() const;
			bool should_log() const;
			bool color_support() const;
			
			void print_timestamp_if_necessary(const TimeStamp& timestamp, bool auto_timestamp = false);
			void print_auto_timestamp_if_necessary();
	};
}

#endif

/******************************************************************************/

